﻿using CocoaAni.Net.WebSockets.Enums;

namespace CocoaAni.Net.WebSockets.Frames;

public abstract class Frame : FrameHeader
{
    public Memory<byte> Payload => Buffer.AsMemory(PayloadStartIndex, PayloadLength);
    public byte[] Buffer { get; set; } = null!;
    public int PayloadStartIndex { get; set; }
    public static byte[]? DefaultMasks { get; set; } = { 25, 78, 126, 11 };

    protected Frame(int size) : base(size)
    {
    }

    protected Frame() : base(0)
    {
    }

    protected Frame(byte[] headerBuffer) : base(headerBuffer)
    {
    }

    protected Frame(byte[] headerBuffer, int headerStartIndex) : base(headerBuffer, headerStartIndex)
    {
    }

    public static SendFrame CreateSendFrame(
        byte[] data, int offset, int dataLen,
        SendFrameBuildMode saveMode,
        bool isFin, WSOpcode opcode, byte[]? masks) =>
        new SendFrame(data, offset, dataLen, saveMode, isFin, opcode, masks);

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static bool GetHasMask(byte data) => (data & 0x80) == 0x80;

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static WSOpcode GetOpcode(byte data) => (WSOpcode)(data & 0x0F);

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static bool GetFin(byte data) => (data & 0x80) == 0x80;

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static bool GetRsv1(byte data) => (data & 0x40) == 0x40;

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static bool GetRsv2(byte data) => (data & 0x20) == 0x20;

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static bool GetRsv3(byte data) => (data & 0x10) == 0x10;

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static long GetPayloadLength(byte[] buffer, int headStartPos)
    {
        long len = buffer[headStartPos + 1] & 0x7F;
        switch (len)
        {
            case 126:
                len = (buffer[headStartPos + 2] << 8) + buffer[headStartPos + 3];
                break;

            case 127:
                for (var i = 2; i < 10; i++)
                {
                    len <<= 8;
                    len |= buffer[headStartPos + i];
                }
                break;

            default: break;
        }
        return len;
    }

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static byte GetPayloadLengthOrMode(byte data) => (byte)(data & 0x7F);

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static int CalculateHeaderSize(bool hasMask, ulong dataLength)
    {
        return (dataLength <= 125 ? 2 : dataLength <= 0xFFFF ? 4 : 10) + (hasMask ? 4 : 0);
    }

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static int CalculateHeaderSize(bool hasMask, int dataLength)
    {
        return (dataLength <= 125 ? 2 : dataLength <= 0xFFFF ? 4 : 10) + (hasMask ? 4 : 0);
    }
}